import type { Router, RouteLocationNormalized } from 'vue-router';
import { useAppStoreWithOut } from '/@/store/modules/app';
import { useUserStoreWithOut } from '/@/store/modules/user';
import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
import { Modal, notification } from 'ant-design-vue';
import { warn } from '/@/utils/log';
import { unref } from 'vue';
import { setRouteChange } from '/@/logics/mitt/routeChange';
import { createPermissionGuard } from './permissionGuard';
import { createStateGuard } from './stateGuard';
import nProgress from 'nprogress';
import projectSetting from '/@/settings/projectSetting';
import { createParamMenuGuard } from './paramMenuGuard';

// Don't change the order of creation
export function setupRouterGuard(router: Router) {
  createPageGuard(router);
  createPageLoadingGuard(router);
  createHttpGuard(router);
  createScrollGuard(router);
  createMessageGuard(router);
  createProgressGuard(router);
  createPermissionGuard(router);
  createParamMenuGuard(router); // must after createPermissionGuard (menu has been built.)
  createStateGuard(router);
}

/**
 * 创建页面守卫函数
 *
 * 该函数用于在 Vue 路由跳转前后做一些处理，主要用于记录页面加载状态，优化路由跳转性能。
 * 当页面首次加载后，它的状态将会被缓存在一个 Map 对象中，之后的每次路由跳转前都会检查这个状态，
 * 已经加载过的页面将不会进行额外的加载处理。
 *
 * @param {Router} router - Vue Router 实例，负责管理页面路由跳转
 */
function createPageGuard(router: Router) {
  // 用于存储页面加载状态的 Map 对象，键是页面的路径，值是布尔值表示该页面是否已加载
  const loadedPageMap = new Map<string, boolean>();

  // 路由守卫：在每次导航之前被调用
  router.beforeEach(async (to) => {
    // 如果页面已加载，则将 `loaded` 标记设置为 true，以便页面可以快速打开，不需要重新加载和处理
    to.meta.loaded = !!loadedPageMap.get(to.path);
    // 通知路由变更事件，这里的 `setRouteChange` 函数需要在其他地方定义，用于处理路由变更事件
    setRouteChange(to);

    return true; // 返回 true 允许路由跳转继续
  });

  // 路由守卫：在每次导航之后被调用
  router.afterEach((to) => {
    // 设置页面为已加载状态，下次访问该页面时，`beforeEach` 守卫中的加载逻辑将不会被触发
    loadedPageMap.set(to.path, true);
  });
}

/**
 * 创建页面加载守卫
 *
 * @param {Router} router - Vue Router 实例
 */
function createPageLoadingGuard(router: Router) {
  // 使用 userStore 和 appStore
  const userStore = useUserStoreWithOut();
  const appStore = useAppStoreWithOut();

  // 获取转场设置中的开启页面加载状态的函数
  const { getOpenPageLoading } = useTransitionSetting();

  // 导航守卫：在每次路由切换前执行
  router.beforeEach(async (to) => {
    // 如果用户未登录（没有 token），直接通过跳过判断
    if (!userStore.getToken) {
      return true;
    }

    // 如果该路由已经加载过（`to.meta.loaded` 为 `true`），直接通过跳过判断
    if (to.meta.loaded) {
      return true;
    }

    // 如果设置开启页面加载状态
    if (unref(getOpenPageLoading)) {
      // 设置页面加载中的状态
      appStore.setPageLoadingAction(true);
      return true;
    }

    // 默认通过跳过判断
    return true;
  });

  // 导航守卫：在每次路由切换后执行
  router.afterEach(async () => {
    // 如果设置开启页面加载状态
    if (unref(getOpenPageLoading)) {
      // TODO 寻找更好的方式
      // 使用定时器模拟加载时间，防止过快闪烁
      setTimeout(() => {
        // 取消页面加载中的状态
        appStore.setPageLoading(false);
      }, 220);
    }

    // 默认通过跳过判断
    return true;
  });
}

/**
 * 创建 HTTP 守卫,切换路由时关闭当前页面以完成请求的接口
 *
 * @param {Router} router - Vue Router 实例
 */
function createHttpGuard(router: Router) {
  // 获取项目设置中的 removeAllHttpPending 属性
  const { removeAllHttpPending } = projectSetting;

  // 可空的 AxiosCanceler 实例
  let axiosCanceler: Nullable<AxiosCanceler>;

  // 如果需要移除所有已挂起的 HTTP 请求
  if (removeAllHttpPending) {
    // 创建一个 AxiosCanceler 实例
    axiosCanceler = new AxiosCanceler();
  }

  // 导航守卫：在每次路由切换前执行
  router.beforeEach(async () => {
    // 切换路由时，删除之前的请求
    axiosCanceler?.removeAllPending();
    return true;
  });
}

/**
 * 创建滚动守卫,Routing switch back to the top
 *
 * @param {Router} router - Vue Router 实例
 */
function createScrollGuard(router: Router) {
  /**
   * 判断是否为哈希链接
   *
   * @param {string} href - 链接地址
   * @returns {boolean} - 是否为哈希链接
   */
  const isHash = (href: string) => {
    return /^#/.test(href);
  };

  // 获取文档的 `<body>` 元素
  const body = document.body;

  // 导航守卫：在每次路由切换后执行
  router.afterEach(async (to) => {
    // 如果目标路由地址为哈希链接，则滚动到页面顶部
    isHash((to as RouteLocationNormalized & { href: string })?.href) && body.scrollTo(0, 0);

    // 默认通过跳过判断
    return true;
  });
}

/**
 * 创建消息守卫,Used to close the message instance when the route is switched
 *
 * @param {Router} router - Vue Router 实例
 */
export function createMessageGuard(router: Router) {
  // 获取项目设置中的 closeMessageOnSwitch 属性
  const { closeMessageOnSwitch } = projectSetting;

  // 导航守卫：在每次路由切换前执行
  router.beforeEach(async () => {
    try {
      // 如果设置了 closeMessageOnSwitch 属性为 true
      if (closeMessageOnSwitch) {
        // 销毁所有的模态框
        Modal.destroyAll();

        // 销毁所有的通知
        notification.destroy();
      }
    } catch (error) {
      // 捕获异常并记录警告信息
      warn('message guard error:' + error);
    }

    // 默认通过跳过判断
    return true;
  });
}

/**
 * 创建进度守卫
 *
 * @param {Router} router - Vue Router 实例
 */
export function createProgressGuard(router: Router) {
  // 获取过渡设置中的 getOpenNProgress 方法
  const { getOpenNProgress } = useTransitionSetting();

  // 导航守卫：在每次路由切换前执行
  router.beforeEach(async (to) => {
    // 如果目标路由的 meta 字段上有 loaded 属性，则直接通过跳过判断
    if (to.meta.loaded) {
      return true;
    }

    // 如果 getOpenNProgress 方法存在，则开始进度条
    unref(getOpenNProgress) && nProgress.start();

    // 默认通过跳过判断
    return true;
  });

  // 导航守卫：在每次路由切换后执行
  router.afterEach(async () => {
    // 如果 getOpenNProgress 方法存在，则完成进度条
    unref(getOpenNProgress) && nProgress.done();

    // 默认通过跳过判断
    return true;
  });
}
